/**
 *  This file is part of PennerBot
 *
 *  Copyright (C) 2010 microkernel
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <core/convert.hpp>
#include <core/messages.hpp>
#include <core/portable.hpp>
#include <core/regex.hpp>

#include <core/modules/bottlecollect.hpp>


namespace PBot
{
namespace Core
{

BottleCollect::BottleCollect(Connection& _con, Config* _cfg)
 : Module(_con, _cfg), paused(false), antiCaptcha(AntiCaptcha::GetInstance()), junkUsed(false), noOldJunk(false), noJunkEngine(false), failedAttempts(0)
{
    moduleName = "BottleCollect";
}

BottleCollect::~BottleCollect()
{
}

bool BottleCollect::Do()
{
    if(!cfg->BC_enabled && !paused)
    {
        listener.Dispatch(cfg->id, MSG_BC_DISABLED);
        paused = true;
        return true;
    }
    else if(!cfg->BC_enabled && paused)
    {
        return true;
    }
    else if(cfg->BC_enabled && paused)
    {
        listener.Dispatch(cfg->id, MSG_BC_ENABLED);
        paused = false;
    }

    listener.Dispatch(cfg->id, MSG_BC_STARTING);

    while(failedAttempts < MAX_BC_ATTEMPTS)
    {
        std::string url = cfg->website + "/overview/";
        if(!con.Get(url))
        {
            ++failedAttempts;
            continue;
        }

        PortableSleep(1,3);

        int counter;

        // Collect counter
        if(!str.GetRegex(cfg->game, Strings::BC_COUNTER)->Search(con.GetBuffer(), counter))
        {
            listener.Dispatch(cfg->id, MSG_BC_COUNTERCHD);
            ++failedAttempts;
            continue;
        }

        // Already collecting?
        if(counter > 0)
        {
            listener.Dispatch(cfg->id, MSG_BC_ALREADYCOLL, Sleep(counter+10, counter+30));
            failedAttempts = 0;
            return true;
        }

        // Check if fighting
        if(!str.GetRegex(cfg->game, Strings::BC_FIGHTCOUNTER)->Search(con.GetBuffer(), counter))
        {
            listener.Dispatch(cfg->id, MSG_BC_COUNTERCHD);
            ++failedAttempts;
            continue;
        }

        if(counter > 0)
        {
            listener.Dispatch(cfg->id, MSG_BC_FIGHTING, Sleep(counter+10, counter+30));
            failedAttempts = 0;
            return true;
        }

        if(cfg->BC_usejunk && !junkUsed && !noJunkEngine)
        {
            listener.Dispatch(cfg->id, MSG_BC_TRYCHANGEJUNK);

            url = cfg->website + "/stock/";

            if(!con.Get(url))
            {
                ++failedAttempts;
                continue;
            }

            PortableSleep(1,3);

            url = cfg->website + "/stock/plunder/";

            if(!con.Get(url))
            {
                ++failedAttempts;
                continue;
            }

            PortableSleep(1,3);

            if(!str.GetRegex(cfg->game, Strings::BC_OLDJUNK)->Search(con.GetBuffer(), oldjunk))
            {
                // no junk used before
                noOldJunk = true;
            }

            if(oldjunk.find(str.GetString(cfg->game, Strings::BC_JUNKENGINE)) == std::string::npos)
            {
                unsigned int junkID;

                if(str.GetRegex(cfg->game, Strings::BC_JUNKENGINE_RX)->Search(con.GetBuffer(), junkID))
                {
                    if(!ChangeJunk(junkID))
                    {
                        ++failedAttempts;
                        continue;
                    }
                    else
                    {
                        listener.Dispatch(cfg->id, MSG_BC_CHANGEDJUNK);
                        junkUsed = true;

                    }
                }
                else
                {
                    noJunkEngine = true;
                    listener.Dispatch(cfg->id, MSG_BC_JUNKNOTFOUND);
                }
            }
            else
            {
                listener.Dispatch(cfg->id, MSG_BC_ALREADYJUNK);
            }

            PortableSleep(1,3);
        }



        url = cfg->website + "/activities/";

        if(!con.Get(url))
        {
            ++failedAttempts;
            continue;
        }

        // Check if committing a crime
        if(str.GetRegex(cfg->game, Strings::BC_CRIMECOUNTER)->Search(con.GetBuffer(), counter))
        {
            listener.Dispatch(cfg->id, MSG_BC_CRIME, Sleep(counter+10, counter+30));
            failedAttempts = 0;
            noJunkEngine = false;
            return true;
        }

        // Check if form changed
        if(!str.GetRegex(cfg->game, Strings::BC_FORMCHECK)->Match(con.GetBuffer()))
        {
            listener.Dispatch(cfg->id, MSG_BC_FORMCHD);
            ++failedAttempts;
            continue;
        }

        if(con.GetBuffer().find(str.GetString(cfg->game, Strings::BC_SHOPPINGCART)) != std::string::npos)
        {

            listener.Dispatch(cfg->id, MSG_BC_EMPTYOUTCART);

            std::string type;
            if(!str.GetRegex(cfg->game, Strings::BC_CART_TYPE)->Search(con.GetBuffer(), type))
            {
                listener.Dispatch(cfg->id, MSG_BC_CARTRXFAIL);
                ++failedAttempts;
                continue;
            }

            std::string pending;
            if(!str.GetRegex(cfg->game, Strings::BC_CART_PENDING)->Search(con.GetBuffer(), pending))
            {
                listener.Dispatch(cfg->id, MSG_BC_CARTRXFAIL);
                ++failedAttempts;
                continue;
            }

            std::string cartsubmit;
            if(!str.GetRegex(cfg->game, Strings::BC_CART_SUBMIT)->Search(con.GetBuffer(), cartsubmit))
            {
                listener.Dispatch(cfg->id, MSG_BC_CARTRXFAIL);
                ++failedAttempts;
                continue;
            }

            std::string posturl;
            if(!str.GetRegex(cfg->game, Strings::BC_CART_ACTION)->Search(con.GetBuffer(), posturl))
            {
               listener.Dispatch(cfg->id, MSG_BC_CARTRXFAIL);
               ++failedAttempts;
               continue;
            }


            std::string postdata;
            postdata = "type="+ con.Escape(type) +"&time=10&bottlecollect_pending="+con.Escape(pending)+"&Submit2="+ con.Escape(cartsubmit);
            if(!con.Post(cfg->website + posturl, postdata))
            {
                ++failedAttempts;
                continue;
            }
        }

        PortableSleep(1,2);

        // Get concentration value
        std::string formconc;
        if(!str.GetRegex(cfg->game, Strings::BC_FORMCONC)->Search(con.GetBuffer(), formconc))
        {
            listener.Dispatch(cfg->id, MSG_BC_FORMCHD);
            ++failedAttempts;
            continue;
        }

        // Get form action
        std::string formaction;
        if(!str.GetRegex(cfg->game, Strings::BC_FORMACTION)->Search(con.GetBuffer(), formaction))
        {
            listener.Dispatch(cfg->id, MSG_BC_FORMCHD);
            ++failedAttempts;
            continue;
        }

        // Check if concentration is enabled or 30min probability is active

        unsigned int collectTime = 10;

        if(str.GetRegex(cfg->game, Strings::BC_CONCENTRATION)->Match(con.GetBuffer()))
        {
            listener.Dispatch(cfg->id, MSG_BC_CONCENTRATION);
            collectTime = 60;
        }
        else if(random(1,100) <= cfg->BC_thirtyminprobability)
        {
            listener.Dispatch(cfg->id, MSG_BC_THIRTYMIN);
            collectTime = 30;
        }

        url = cfg->website+formaction;

        std::string postdata;

        if(cfg->hasCaptcha)
        {
            // Get captcha url
            if(!str.GetRegex(cfg->game, Strings::BC_CAPTCHAURL)->Search(con.GetBuffer(), url))
            {
                listener.Dispatch(cfg->id, MSG_BC_CAPTCHACHD);
                ++failedAttempts;
                continue;
            }

            // Download captcha

            std::string filename = "captchas/captcha_"+cfg->botname+".jpg";

            if(!con.GetFile(url, filename, false))
            {
                ++failedAttempts;
                continue;
            }

            int x,y;

            // Crack captcha

            switch(antiCaptcha.Crack(filename, x, y))
            {
                case AntiCaptcha::CRACKFAIL:
                    listener.Dispatch(cfg->id, MSG_AC_CRACKFAIL);
                    ++failedAttempts;
                    continue;

                case AntiCaptcha::IOERROR:
                    listener.Dispatch(cfg->id, MSG_AC_IOERROR, filename);
                    ++failedAttempts;
                    continue;

                case AntiCaptcha::CRACKOKAY:
                    postdata = "sammeln="+NumToStr(collectTime)+"&konzentrieren="+formconc+"&captchacheck.x="+NumToStr(x)+"&captchacheck.y=" + NumToStr(y);
                    if(!con.Post(url, postdata))
                    {
                        ++failedAttempts;
                        continue;
                    }

                    if(!str.GetRegex(cfg->game, Strings::BC_COUNTER)->Search(con.GetBuffer(), counter))
                    {
                        listener.Dispatch(cfg->id, MSG_BC_COUNTERCHD);
                        ++failedAttempts;
                        continue;
                    }

                    // Check if started to collect
                    if(counter > 0)
                    {
                        listener.Dispatch(cfg->id, MSG_BC_STARTED, Sleep(counter+cfg->BC_minwait, counter+cfg->BC_maxwait));
                        failedAttempts = 0;
                        noJunkEngine = false;

                        if(cfg->BC_usejunk && junkUsed && !noOldJunk)
                        {
                            PortableSleep(1,3);

                            url = cfg->website + "/stock/";

                            if(!con.Get(url))
                            {
                                ++failedAttempts;
                                continue;
                            }

                            PortableSleep(1,3);

                            url = cfg->website + "/stock/plunder/";

                            if(!con.Get(url))
                            {
                                ++failedAttempts;
                                continue;
                            }

                            PortableSleep(1,3);

                            Regex junkID_RX(str.GetString(cfg->game, Strings::BC_JUNKID_FRONT) + oldjunk + str.GetString(cfg->game, Strings::BC_JUNKID_BACK));

                            unsigned int junkID;

                            if(junkID_RX.Search(con.GetBuffer(), junkID))
                            {
                                if(!ChangeJunk(junkID))
                                {
                                    ++failedAttempts;
                                    continue;
                                }
                                else
                                {
                                    listener.Dispatch(cfg->id, MSG_BC_CHANGEDJUNKBACK);
                                    junkUsed = false;
                                }
                            }
                            else
                            {
                                listener.Dispatch(cfg->id, MSG_BC_JUNKIDCHD);
                            }

                        }

                        return true;
                    }
                    else
                    {
                        listener.Dispatch(cfg->id, MSG_AC_CRACKFAIL);
                        ++failedAttempts;
                        continue;
                    }

                    break;
            }

        }
        else
        {
            postdata = "sammeln="+NumToStr(collectTime)+"&konzentrieren="+formconc;

            if(!con.Post(url, postdata))
            {
                ++failedAttempts;
                continue;
            }

            if(!str.GetRegex(cfg->game, Strings::BC_COUNTER)->Search(con.GetBuffer(), counter))
            {
                listener.Dispatch(cfg->id, MSG_BC_COUNTERCHD);
                ++failedAttempts;
                continue;
            }

            listener.Dispatch(cfg->id, MSG_BC_STARTED, Sleep(counter+cfg->BC_minwait, counter+cfg->BC_maxwait));
            failedAttempts = 0;
            noJunkEngine = false;

            if(cfg->BC_usejunk && junkUsed && !noOldJunk)
            {
                PortableSleep(1,3);

                url = cfg->website + "/stock/";

                if(!con.Get(url))
                {
                    ++failedAttempts;
                    continue;
                }

                PortableSleep(1,3);

                url = cfg->website + "/stock/plunder/";

                if(!con.Get(url))
                {
                    ++failedAttempts;
                    continue;
                }

                PortableSleep(1,3);

                Regex junkID_RX(str.GetString(cfg->game, Strings::BC_JUNKID_FRONT) + oldjunk + str.GetString(cfg->game, Strings::BC_JUNKID_BACK));

                unsigned int junkID;

                if(junkID_RX.Search(con.GetBuffer(), junkID))
                {
                    if(!ChangeJunk(junkID))
                    {
                        ++failedAttempts;
                        continue;
                    }
                    else
                    {
                        listener.Dispatch(cfg->id, MSG_BC_CHANGEDJUNKBACK);
                        junkUsed = false;
                    }
                }
                else
                {
                    listener.Dispatch(cfg->id, MSG_BC_JUNKIDCHD);
                }

            }



            return true;
        }

    }

    listener.Dispatch(cfg->id, MSG_BC_TOOMANYATTEMPTS);
    failedAttempts = 0;
    noJunkEngine = false;

    Sleep(10*60);

    return false;
}

bool BottleCollect::ChangeJunk(unsigned int junkID)
{
    std::string action;
    if(!str.GetRegex(cfg->game, Strings::BC_JUNKFORMACTION)->Search(con.GetBuffer(), action))
    {
        listener.Dispatch(cfg->id, MSG_BC_JUNKFORMACTCHD);
        return false;
    }

    std::string idname;
    if(!str.GetRegex(cfg->game, Strings::BC_JUNKFORMID)->Search(con.GetBuffer(), idname))
    {
        listener.Dispatch(cfg->id, MSG_BC_JUNKFORMIDCHD);
        return false;
    }

    std::string url = cfg->website + action;
    std::string postdata = idname+"="+NumToStr(junkID);

    if(!con.Post(url, postdata))
    {
        return false;
    }

    return true;
}

}
}
